package com.zhang.common.generator.dbid;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import com.zhang.common.domain.IdInfo;
import com.zhang.common.service.IdInfoService;
import com.zhang.common.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * IdGenerator
 *
 * @author zhangxin
 * @date 2021/5/1
 */
@Component
public class IdGenerator {
    private final Logger logger = LoggerFactory.getLogger(IdGenerator.class);
    private Map<String, LinkedList<String>> cacheMap = new HashMap();
    @Autowired
    private IdInfoService idInfoServiceImpl;
    private static final int SEQUENCE_MAX_LENGTH = 18;

    public IdGenerator() {
    }

    public String generateInternalId32() {
        return this.generateFlowId32("SYS", "INTERNAL_ID");
    }

    public String generateFlowId24() {
        return this.generateFlowId("", "FLOW_ID", 24);
    }

    public String generateFlowId24(String prefix, String idType) {
        return this.generateFlowId(prefix, idType, 24);
    }

    public String generateFlowId32() {
        return this.generateFlowId("", "FLOW_ID", 32);
    }

    public String generateFlowId32(String prefix, String idType) {
        return this.generateFlowId(prefix, idType, 32);
    }

    public String generateFlowId(int idLength) {
        return this.generateFlowId("", "FLOW_ID", idLength);
    }

    public String generateFlowId(String idType, int idLength) {
        return this.generateFlowId("", idType, idLength);
    }

    public String generateFlowId(String prefix, String idType, int idLength) {
        long millis = System.currentTimeMillis();
        String preStr = StringUtils.isBlank(prefix) ? Long.toString(millis) : prefix.concat(Long.toString(millis));
        idLength -= preStr.length();
        String flowIdSeq = this.generate(idType, idLength);
        StringBuilder builder = new StringBuilder();
        builder.append(preStr).append(flowIdSeq);
        return builder.toString();
    }

    private synchronized String generate(String idType, int idLength) {
        LinkedList<String> cacheList = (LinkedList)this.cacheMap.get(idType.concat(String.valueOf(idLength)));
        if (cacheList == null) {
            cacheList = new LinkedList();
        }

        if (CollectionUtils.isEmpty(cacheList)) {
            this.init(cacheList, idType, idLength);
        }

        return (String)cacheList.removeFirst();
    }

    private void init(LinkedList<String> cacheList, String idType, int idLength) {
        if (cacheList.isEmpty()) {
            cacheList.clear();
            IdInfo idInfo = this.idInfoServiceImpl.findAndUpdateIdInfo(idType);
            Assert.notNull(idInfo, String.format("idType:%s 流水号表中未配置!", idType));
            Long currMaxId = idInfo.getMaxId() + 1L;
            if (String.valueOf(currMaxId).length() > idLength) {
                currMaxId = 1L;
            }

            Integer step = idInfo.getStep();

            for(int i = 0; i < step; ++i) {
                currMaxId = currMaxId + (long)i;
                String currentValueStr = String.valueOf(currMaxId);
                if (currentValueStr.length() > idLength) {
                    break;
                }

                currentValueStr = StringUtils.leftPadZero(currentValueStr, idLength);
                cacheList.add(currentValueStr);
            }

            this.cacheMap.put(idType.concat(String.valueOf(idLength)), cacheList);
        }
    }
}
